<?php

/**
 * Copyright (C) 2007 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

$currentDir = dirname(__FILE__);

require_once($currentDir . "/utilities.php");
/**
 * @author api.jmatt@gmail.com
 * @version 3.0
 */
class SensapilityAuthentication{
  
  var $developerEmail;
  var $developerPassword;
  var $pubIds;
  var $debugMode = false;
  var $debugArray = array('ns1:debug_zip' => 12345,
                          'ns1:debug_phone' => 67890,
                          'ns1:debug_association_type' => 'Active');
  /**
   * Constructor that initializes authentication object. If at least one pub
   * id is set, it will call setPubIds to attempt to fully populate the pub
   * id array. Also calls setClientHeaders() to set as many service headers
   * as ids are available for.
   * @param string $email The developer email address
   * @param string $password The developer password
   * @param array $id Associative array mapping service names (ContentAds,
   * SearchAds, ReferralAds) to publisher ids
   * @return none
   */
  function SensapilityAuthentication($email = null, 
                                     $password = null, 
                                     $ids = array()) {
    if ( !is_null($email) &&
         strcmp(trim($email), '') != 0 ) {
      $this->developerEmail = $email;
    } else {
      $this->developerEmail = null;
    }

    if ( !is_null($password) &&
         strcmp(trim($password), '') != 0 ) {
      $this->developerPassword = $password;
    } else {
      $this->developerPassword = null;
    }

    $this->pubIds = array('ContentAds' => null,
                          'SearchAds' => null,
                          'ReferralAds' => null);
    $this->setPubIds($ids, false);
    
    $this->setClientHeaders();
    $keys = array_keys($this->pubIds);
  }
  
  
  /**
   * If we have developer email, developer password, and at least one pub id,
   * this method looks up any missing ids in the publisher array. Accesses the
   * pubIds array. Accesses $SENSAPILITY_CLIENTS global to use the account
   * service. This method will print errors if any of the required data 
   * elements are missing from this instance
   * @return none
   */
  function __lookupIds() {
    global $SENSAPILITY_CLIENTS;    
    
    // we must have a developer email, a password, and at least one service id
    $keys = array_keys($this->pubIds);
    $idCount = 0;
    foreach ( $keys as $key ) {
      if ( !is_null($this->pubIds[$key]) ) {
        $idCount++;
      }
    }
    // if not, we just put out a warning and fail to finish populating the array
    if ( is_null($this->developerPassword) ||
         is_null($this->developerEmail) ||
         $idCount == 0 ) {
      
      if ( is_null($this->developerPassword) ) {
        echo 'SensAPIlity PHP library => Warning: Service id lookup failed ' .
             'because there was no developer password available, some ' .
             'SensAPIlity services will be unavailable until password is ' . 
             "set.\n";
      }
      if ( is_null($this->developerEmail) ) {
        echo 'SensAPIlity PHP library => Warning: Service id lookup failed ' . 
              'because there was no developer email available, some' .
              "SensAPIlity services will be unavailable until email is set.\n";
      }
      if ( $idCount == 0 ) {
        echo 'SensAPIlity PHP library => Warning: Service id lookup failed ' . 
             ' because at least one service id must be specified to lookup ' . 
             'the others. Some services may be unavailable until ' . 
             "their service ids are supplied.\n";
      }
      return;
    }
    
    // create the account service header
    $developerEmailHeader = array('value' => $this->developerEmail, 
                                  'attributes' => 
                                  array('SOAP-ENV:actor' => 
                                        'http://schemas.xmlsoap.org/soap/' . 
                                        'actor/next', 
                                        'SOAP-ENV:mustUnderstand' => 0)
                                  );
    
    $developerPassHeader = array('value' => $this->developerPassword, 
                                 'attributes' => 
                                 array('SOAP-ENV:actor' => 
                                       'http://schemas.xmlsoap.org/soap/' .
                                       'actor/next', 
                                       'SOAP-ENV:mustUnderstand' => 0)
                                 );
    // find a valid client id value    
    $clientIdVal = null;
    $keys = array_keys($this->pubIds);
    foreach ( $keys as $key ) {
      if ( !is_null($this->pubIds[$key]) && is_null($clientIdVal) ) {
        $clientIdVal = $this->pubIds[$key];
      }
    }
    $clientIdHeader = array('value' => $clientIdVal, 
                            'attributes' => 
                            array('SOAP-ENV:actor' => 
                                  'http://schemas.xmlsoap.org/soap/actor/next',
                                  'SOAP-ENV:mustUnderstand' => 0)
                            );
  
    $acctHeaders = array('ns1:developer_email' => $developerEmailHeader, 
                         'ns1:developer_password' => $developerPassHeader, 
                         'ns1:client_id' => $clientIdHeader);

    $headerXML = ArrayToXml($acctHeaders);
    $SENSAPILITY_CLIENTS->setHeaders('Account', $headerXML);
    $acctService = $SENSAPILITY_CLIENTS->getService('Account');

    $foundIds =  $acctService->getAllSyndicationServices();
    if ( is_array($foundIds) ) {
      $keys = array_keys($foundIds);
      foreach ( $keys as $key ) {
        if ( array_key_exists($foundIds[$key]['type'], $this->pubIds) ) {
          $this->pubIds[$foundIds[$key]['type']] = $foundIds[$key]['id'];
        }
      }
    }
  }
  
  /**
   * Sets the client headers for as many pub ids as we have. Access the 
   * $SENSAPILITY_CLIENTS global variable to set headers.
   * @return none
   */
  function setClientHeaders() {
    global $SENSAPILITY_CLIENTS;
    // build the header for AFC requests, also use this ehader for non-specific
    // id requests
    $requestHeaderAttributes = array('SOAP-ENV:actor' => 
                                         'http://schemas.xmlsoap.org/soap/' . 
                                         'actor/next', 
                                     'SOAP-ENV:mustUnderstand' => 0);
    
    $developerEmailHeader = array('value' => $this->developerEmail, 
                                  'attributes' => $requestHeaderAttributes);
    
    $developerPassHeader = array('value' => $this->developerPassword, 
                                 'attributes' => $requestHeaderAttributes);
    
    if ( !is_null($this->pubIds['ContentAds']) ) {
      $clientIdHeader = array('value' => $this->pubIds['ContentAds'], 
                              'attributes' => $requestHeaderAttributes);
      
      $AFCHeaders = array('ns1:developer_email' =>  $developerEmailHeader, 
                          'ns1:developer_password' => $developerPassHeader, 
                          'ns1:client_id' => $clientIdHeader);
      
      $AFCHeaderXML = ArrayToXml($AFCHeaders);
      if ( $this->debugMode ) {
        $SENSAPILITY_CLIENTS->setHeaders('Account', 
                                        $AFCHeaderXML . 
                                        ArrayToXml($this->debugArray));
      } else {
        $SENSAPILITY_CLIENTS->setHeaders('Account', $AFCHeaderXML);
      }
      $this-> __setHeadersOnGeneralServices($AFCHeaderXML);
      $SENSAPILITY_CLIENTS->setHeaders('AdSenseForContent', $AFCHeaderXML);
    } else {
      // just erase the old headers
      $this-> __setHeadersOnGeneralServices('');
      $SENSAPILITY_CLIENTS->setHeaders('AdSenseForContent', '');
      
      // the account service may not require a service id, set it with just
      // the developer information, this may be replaced later by a complete
      // header but the account service will never be headless
      $acctServiceHeader = array('ns1:developer_email' => 
                                     $developerEmailHeader, 
                                 'ns1:developer_password' => 
                                     $developerPassHeader);
      if ( $this->debugMode ) {
        $SENSAPILITY_CLIENTS->setHeaders('Account', 
                                         ArrayToXml($acctServiceHeader) . 
                                         ArrayToXml($this->debugArray));
      } else {
        $SENSAPILITY_CLIENTS->setHeaders('Account',
                                         ArrayToXml($acctServiceHeader));
      }
    }
    
    if ( !is_null($this->pubIds['SearchAds']) ) {
      $clientIdHeader = array('value' => $this->pubIds['SearchAds'], 
                              'attributes' => $requestHeaderAttributes);
      
      $AFSHeaders = array('ns1:developer_email' => $developerEmailHeader, 
                          'ns1:developer_password' => $developerPassHeader, 
                          'ns1:client_id' => $clientIdHeader);
      
      $AFSHeaderXML = ArrayToXml($AFSHeaders);
      $SENSAPILITY_CLIENTS->setHeaders('AdSenseForSearch', $AFSHeaderXML);
      
      // if we weren't able to set these service headers with the Content Ads id
      // use the searchads one instead.
      if ( is_null($this->pubIds['ContentAds']) ) {
        if ( $this->debugMode ) {
          $SENSAPILITY_CLIENTS->setHeaders('Account', 
                                           $AFSHeaderXML . 
                                           ArrayToXml($this->debugArray));
        } else {
          $SENSAPILITY_CLIENTS->setHeaders('Account', $AFSHeaderXML);
        }
        $this-> __setHeadersOnGeneralServices($AFSHeaderXML);
      }
    } else {
      $SENSAPILITY_CLIENTS->setHeaders('AdSenseForSearch', '');
    }
    
    if ( !is_null($this->pubIds['ReferralAds']) ) {
      $clientIdHeader = array('value' => $this->pubIds['ReferralAds'], 
                              'attributes' => $requestHeaderAttributes);
      
      $referralHeaders = array('ns1:developer_email' => $developerEmailHeader, 
                               'ns1:developer_password' =>
                                   $developerPassHeader, 
                               'ns1:client_id' => $clientIdHeader);
      
      $referralHeaderXML = ArrayToXml($referralHeaders);
      $SENSAPILITY_CLIENTS->setHeaders('Referral', $referralHeaderXML);
      
      // if we weren't able to set these service headers with the Content Ads or
      // Search Ads id use the referralAds one instead.
      if ( is_null($this->pubIds['ContentAds']) && 
           is_null($this->pubIds['SearchAds']) ) {
        if ( $this->debugMode ) {
          $SENSAPILITY_CLIENTS->setHeaders('Account', 
                                           $referralHeaderXML . 
                                           ArrayToXml($this->debugArray));
        } else {
          $SENSAPILITY_CLIENTS->setHeaders('Account', $referralHeaderXML);
        }
        $this-> __setHeadersOnGeneralServices($referralHeaderXML);

      }
    } else {
      $SENSAPILITY_CLIENTS->setHeaders('Referral', '');
    }
  }
  
  /**
   * Sets the developer email address
   * @param string $newEmail The email address to set the developer email to.
   * @param bool $setClientHeaders Whether or not the clients' headers should 
   * be updated. Defaults to true, if resetting multiple header values, 
   * performance will be better if client headers are only reset when the last
   * header value you are updated is updated.
   */
  function setDeveloperEmail($newEmail, $setClientHeaders = true) {
    
    $this->developerEmail = $newEmail;
    if ( $setClientHeaders ) {
      $this->setClientHeaders();
    }
  }
  
  /**
   * Sets the developer email address
   * @param string $newPass The password to set the developer password to.
   * @param bool $setClientHeaders Whether or not the clients' headers should 
   * be updated. Defaults to true, if resetting multiple header values, 
   * performance will be better if client headers are only reset when the last
   * header value you are updated is updated.
   */
  function setDevelopPassword($newPass, $setClientHeaders = true) {
    
    $this->developerPassword = $newPass;
    if ( $setClientHeaders ) {
      $this->setClientHeaders();
    }
  }
  
  /**
   * Set the publisher ids that are set in the request headers.
   * @param array $newIds the new pub ids to use
   * @param bool $setClientHeaders Whether or not the clients' headers should 
   * be updated. Defaults to true, if resetting multiple header values, 
   * performance will be better if client headers are only reset when the last
   * header value you are updated is updated.
   * @param bool $resetHeaders controls whether all the old headers are removed
   * . Setting this to false would allow the pub ids array to be set across 
   * multiple method calls. For example, by call this method once with the 
   * ContentAds pub id, then calling it again with the SearchAds id.  Most of 
   * the time users will call this function to switch authentication contexts 
   * and so the old set of pub ids should be erased by setting this value to 
   * true.
   * @param bool $lookupMissingIds whether to to attempt to populate any 
   * any missing client ids. Set to false if populating the array over 
   * multiple calls where you want to avoid lookup overhead or if you
   * don't care about missing values
   */
  function setPubIds($newIds, 
                     $setClientHeaders = true, 
                     $resetHeaders = true, 
                     $lookupMissingIds = true) {
    // if this isn't an array, just pass in a blank
    if ( !is_array($newIds) ) {
      $newIds = array();
    }

    if ( $resetHeaders ) {
      $keys = array_keys($this->pubIds);
      foreach ( $keys as $key ) {
        $this->pubIds[$key] = null;
      }
    }

    if ( is_array($newIds) ) {
      // set the values in the pubIds array
      $keys = array_keys($this->pubIds);
      foreach ( $keys as $key ) {
        if ( array_key_exists($key, $newIds) && 
             strcmp(trim($newIds[$key]), '') != 0 ) {
          $this->pubIds[$key] = $newIds[$key];
        } else {
          $this->pubIds[$key] = null;
        }
      }
      
      // see if all the values have been set, if not, try to complete the array
      $keys = array_keys($this->pubIds);
      foreach ( $keys as $key ) {
        if ( is_null($this->pubIds[$key]) || 
             strcmp(trim($this->pubIds[$key]), '') == 0 ) {
          if ( $lookupMissingIds ) {
            $this->__lookupIds();
          }
        }
      }
    }
    
    // reset the header values in the clients holder object
    if ( $setClientHeaders ) {
      $this->setClientHeaders();
    }
  }

  /**
   * @return array Associate array mapping from service types to the pub id.
   */
  function getPubIds() {
    return $this->pubIds;
  }

  /**
   * Used to set the debug_association_status header for use in the sandbox
   * @param string $newAssc The association value to use
   * @param bool $setHeaders Whether or not the clients' headers should 
   * be updated. Defaults to true, if resetting multiple header values, 
   * performance will be better if client headers are only reset when the last
   * header value you are updated is updated.
   * @return none
   */
  function setDebugAssociation($newAssc, $setHeaders = true) {
    $this->debugArray['ns1:debug_association_type'] = $newAssc;
    if ( $setHeaders ) {
      $this->setClientHeaders();
    }
  }

  /**
   * Toggles whether debug headers are used. This is useful for building and 
   * testing applications in the sandbox.
   * @param bool $newMode Whether or not debug headers are used.
   * @param bool $setHeaders Whether or not the clients' headers should 
   * be updated. Defaults to true, if resetting multiple header values, 
   * performance will be better if client headers are only reset when the last
   * header value you are updated is updated.
   * @return none
   */
  function setDebugMode($newMode, $setHeaders = true) {
    $this->debugMode = $newMode;
    if ( $setHeaders ) {
      $this->setClientHeaders();
    }
  }

  function setDebugZip($newZip, $setHeaders = true) {
    $this->debugArray['ns1:debug_zip'] = $newZip;
    if ( $setHeaders ) {
      $this->setClientHeaders();
    }
  }

  function setDebugPhone($newPhone, $setHeaders = true) {
    $this->debugArray['ns1:debug_phone'] = $newPhone;
    if ( $setHeaders ) {
      $this->setClientHeaders();
    }
  }
  /** 
   * Set headers on the services that will accept the id from any
   * syndication service.
   * @param string $header The XML snippet that is the header for the service
   * @return none
   */
  function __setHeadersOnGeneralServices($header = null) {
    global $SENSAPILITY_CLIENTS;
    
    $SENSAPILITY_CLIENTS->setHeaders('Channel', $header);
    $SENSAPILITY_CLIENTS->setHeaders('Report', $header);
    $SENSAPILITY_CLIENTS->setHeaders('SiteFilter', $header);
  }
}
?>
